﻿/*******************************************************************************
 * @file usart.c
 * @brief 串口模块
 *******************************************************************************
*/

#include <string.h>
#include <stdlib.h>
#include "usart.h"
#include "systick.h"


/* 串口时钟表 */
static const uint32_t usart_clks[USART_IND_END] = {
    RCC_APB2Periph_USART1, RCC_APB1Periph_USART2, RCC_APB1Periph_USART3
};
static const IRQn_Type usart_irqs[USART_IND_END] = {
    USART1_IRQn, USART2_IRQn, USART3_IRQn
};
/* 串口接收DMA通道表 */
static DMA_Channel_TypeDef *const usartrx_dma_chs[USART_IND_END] = {
    DMA1_Channel5, DMA1_Channel6, DMA1_Channel3
};
/* 串口实例表 */
static UsartType_t *usart_instances[USART_IND_END] = { 0 };


static void _UsartConfig(const UsartType_t *instance, const UsartContig_t *config)
{
    if(!instance ||!config) return;

    /* 使能时钟 */
    if(instance->usartx == USART1)
    {
        RCC_APB2PeriphClockCmd(usart_clks[instance->index], ENABLE);
    }
    else
    {
        RCC_APB1PeriphClockCmd(usart_clks[instance->index], ENABLE);
    }


    /* 串口配置 */
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate            = config->baudrate;
    USART_InitStructure.USART_WordLength          = config->databits;
    USART_InitStructure.USART_StopBits            = config->stopbits;
    USART_InitStructure.USART_Parity              = config->parity;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(instance->usartx, &USART_InitStructure);

    /* 使能串口 */
    USART_Cmd(instance->usartx, ENABLE);
}

static void _UsartDmaConfig(const UsartType_t *instance, DMA_TypeDef *dmax, DMA_Channel_TypeDef *dma_chx)
{
    if(!instance) return;

    /* enable DMA1 */
    if(dmax == DMA1)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    else if(dmax == DMA2)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

    DMA_InitTypeDef DMA_InitStructure;
    DMA_DeInit(dma_chx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&instance->usartx->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)instance->recive_buffer[instance->recive_wirt_ptr];
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize         = instance->recive_buffer_size;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_Init(dma_chx, &DMA_InitStructure);

    /* 使能DMA */
    DMA_Cmd(dma_chx, ENABLE);
}

/**
 * @brief 构造串口实例
 * @param instance 实例
 * @param usartx 串口外设
 * @param recive_buffer 接收缓冲区指针
 * @param recive_buffer_size 缓冲区大小
 * @return -1: 错误, 0: 成功, 1: 串口实例已存在, 2: 内存分配失败
 */
int Usart_Constructor(UsartType_t *instance, USART_TypeDef *usart_periph, uint32_t recive_buffer_size)
{
    if(!instance) return -1;

    /* 配置串口实例 */
    if(usart_periph == USART1)
        instance->index = USART_IND1;
    else if(usart_periph == USART2)
        instance->index = USART_IND2;
    else if(usart_periph == USART3)
        instance->index = USART_IND3;
    else
        return -1;

    /* 注册串口实例 */
    if(usart_instances[instance->index]) return 1;
    usart_instances[instance->index] = instance;

    instance->usartx = usart_periph;

    /* 关联DMA */
    if(usart_periph == UART4)
        instance->dmax = DMA2;
    else
        instance->dmax = DMA1;

    /* 关联DMA通道 */
    instance->dma_chx = usartrx_dma_chs[instance->index];

    /* 开辟缓存区 */
    instance->recive_buffer[0]   = malloc(recive_buffer_size);
    if(!instance->recive_buffer[0]) return 2;
    memset(instance->recive_buffer[0], 0, recive_buffer_size);

    instance->recive_buffer[1]   = malloc(recive_buffer_size);
    if(!instance->recive_buffer[1]) return 2;
    memset(instance->recive_buffer[1], 0, recive_buffer_size);

    instance->recive_buffer_size = recive_buffer_size;

    /* 初始化 */
    instance->recive_len = 0;
    instance->recive_full_ptr = -1;
    instance->recive_wirt_ptr = 0;
    instance->RecvCb = NULL;

    return 0;
}

void Usart_Destructor(UsartType_t *instance)
{
    if(!instance) return;

    /* 释放内存 */
    free(instance->recive_buffer[0]);
    free(instance->recive_buffer[1]);
    instance->recive_buffer[0] = NULL;
    instance->recive_buffer[1] = NULL;

    /* 注销串口实例 */
    usart_instances[instance->index] = NULL;

    /* 关闭DMA */
    DMA_Cmd(instance->dma_chx, DISABLE);
    DMA_DeInit(instance->dma_chx);
    /* 关闭串口 */
    USART_Cmd(instance->usartx, DISABLE);
    USART_DeInit(instance->usartx);
    /* 关闭中断 */
    USART_ITConfig(instance->usartx, USART_IT_RXNE, DISABLE);
    USART_ITConfig(instance->usartx, USART_IT_IDLE, DISABLE);
    USART_DMACmd(instance->usartx, USART_DMAReq_Rx, DISABLE);
    USART_DMACmd(instance->usartx, USART_DMAReq_Tx, DISABLE);
    NVIC_DisableIRQ(usart_irqs[instance->index]);
    /* 关闭串口时钟 */
    if(instance->usartx == USART1)
    {
        RCC_APB2PeriphClockCmd(usart_clks[instance->index], DISABLE);
    }
    else
    {
        RCC_APB1PeriphClockCmd(usart_clks[instance->index], DISABLE);
    }
    /* 关闭DMA时钟 */
    if(instance->dmax == DMA1)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, DISABLE);
    else if(instance->dmax == DMA2)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, DISABLE);
}

/**
 * @brief 串口初始化函数
 * @param[in] instance 串口实例
 * @return -1: 错误, 0: 成功
 */
int Usart_Init(const UsartType_t *instance, const UsartContig_t *config)
{
    if(!instance || !config) return -1;
    
    /* 配置串口 */
    _UsartConfig(instance, config);
    
    /* 配置GPIO */
    if(config->GpioConfig) config->GpioConfig();

    return 0;
}

/**
 * @brief 配置串口中断接收
 * @param instance  串口实例
 * @param cb        接收中断回调函数
 */
void Usart_ConfigRecevIt(UsartType_t *instance, void (*cb)(void))
{
    if(!instance || !cb) return;
    instance->RecvCb = cb;

    /* 使能串口接收中断 */
    USART_ITConfig(instance->usartx, USART_IT_RXNE, ENABLE);
}

/**
 * @brief 初始化空闲中断DMA接收
 * @param instance 串口实例
 */
void USART_IdleDmaInit(const UsartType_t *instance)
{
    if(!instance) return;

    /* 配置DMA */
    _UsartDmaConfig(instance, instance->dmax, instance->dma_chx);
    /* 使能DMA接收 */
    USART_DMACmd(instance->usartx, USART_DMAReq_Rx, ENABLE);

    /* 中断 */
    USART_ITConfig(instance->usartx, USART_IT_IDLE, ENABLE);
    NVIC_SetPriority(usart_irqs[instance->index], 0);
    NVIC_EnableIRQ(usart_irqs[instance->index]);
}

/**
 * @brief 发送数据
 * @param[in] instance 串口实例 
 * @param[in] data 数据
 * @param[in] size 数据大小
 * @param[in] timeout 超时时间, 单位: ms
 * @return -1: 错误, 0: 发送成功, 1: 发送超时
 */
int USART_Send(const UsartType_t *instance, uint8_t *data, uint32_t size, uint32_t timeout)
{
    if(!instance ||!data) return -1;

    while(size--)
    {
        uint32_t oldtick = Systick_GetTick();
        USART_SendData(instance->usartx, *data++);
        while(RESET == USART_GetFlagStatus(instance->usartx, USART_FLAG_TXE))
            if(Systick_GetTick() - oldtick > timeout) return 1;
    }

    return 0;
}

/**
 * @brief 读取数据从DMA
 * @param[in] instance 串口实例 
 * @param[in] data 数据暂存区
 * @return -1: 错误, 0: 无数据, 大于0: 数据大小
 */
int USART_ReadFromDma(UsartType_t *instance, uint8_t *data)
{
    if(!instance || !data) return -1;

    if(instance->recive_full_ptr == -1) return 0;

    __disable_irq();
    uint8_t *ptr = instance->recive_buffer[instance->recive_full_ptr];
    instance->recive_full_ptr = -1;
    uint32_t size = instance->recive_len;
    instance->recive_len = 0;
    __enable_irq();

    memcpy(data, ptr, size);
    memset(ptr, 0, size);

    return size;
}

/**
 * @brief 串口接收中断处理
 * @param usartx 串口号
 */
void Usart_RxHandler(USART_TypeDef *usartx)
{
    UsartType_t *instance = NULL;
    if(usartx == USART1)
    {
        instance = usart_instances[0];
    }
    else if(usartx == USART2)
    {
        instance = usart_instances[1];
    }
    else if(usartx == USART3)
    {
        instance = usart_instances[2];
    }

    instance->RecvCb();
}

/**
 * @brief 串口空闲中断处理函数
 */
void Usart_IdleCallback(USART_TypeDef  *usartx)
{
    UsartType_t *instance = NULL;
    uint32_t len = 0;

    if(usartx == USART1)
    {
        instance = usart_instances[0];
        DMA_ClearFlag(DMA1_FLAG_TC5);
        if(!instance) return;
    }
    else if(usartx == USART2)
    {
        instance = usart_instances[1];
        DMA_ClearFlag(DMA1_FLAG_TC6);
        if(!instance) return;
    }

    /* 关闭DMA */
    DMA_Cmd(instance->dma_chx, DISABLE);

    /* 获取数据长度 */
    uint32_t remain_len = DMA_GetCurrDataCounter(instance->dma_chx);
    len = instance->recive_buffer_size - remain_len;
    instance->recive_len       = len;
    instance->recive_full_ptr  = instance->recive_wirt_ptr;
    instance->recive_wirt_ptr ^= 1;

    /* 重新配置DMA */
    uint32_t new_addr = (uint32_t)instance->recive_buffer[instance->recive_wirt_ptr];
    instance->dma_chx->CMAR = new_addr;
    DMA_SetCurrDataCounter(instance->dma_chx, instance->recive_buffer_size);
    DMA_Cmd(instance->dma_chx, ENABLE);
}

/**
 * @brief 重写串口中断处理函数
 */
void Usart_Handler(USART_TypeDef *usartx)
{
    if(USART_GetITStatus(usartx, USART_IT_RXNE) != RESET)
    {
        USART_ClearITPendingBit(usartx, USART_IT_RXNE);

        Usart_RxHandler(usartx);
    }
    if(USART_GetITStatus(usartx, USART_IT_IDLE) != RESET)
    {
        USART_ReceiveData(usartx);

        Usart_IdleCallback(usartx);
    }
}
